package demo.dragdemo;

import java.awt.AlphaComposite;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

/**
 * 实现组件拖拽的类
 * 
 * 参考swing hacks
 * 
 * @author 唐植超
 * 
 */
public class ControlDragManager extends Object {
	private JFrame frame;
	private JComponent target;
	private ControlGhostGlassPane glass = null;
	private Component dragSource;

	public static final Image right_img = ObjectUtil.getImage("right.png");
	public static final Image wrong_img = ObjectUtil.getImage("wrong.png");

	/**
	 * @param frame
	 *            参数frame为要实现拖拽的frame，frame中一定只能有一个DragManage对象 ， <br>
	 *            如果有多个，就会导致冲突。 <br>
	 *            如果该窗口要实现拖拽功能，必须有一个DragManage对象，<br>
	 * 
	 * @param target
	 *            拖拽目标
	 */
	public ControlDragManager(JFrame frame, JComponent target) {
		this.frame = frame;
		this.target = target;
		target.setLayout(null);
		this.frame.setGlassPane(this.glass = new ControlGhostGlassPane());
	}

	/**
	 * 指定能拖拽的组件
	 * 
	 * 
	 * @param souce
	 *            被拖拽的组件
	 */
	public void canDrag(Component source) {
		DragAction action = null;
		action = new DragAction(glass, target);
		source.addMouseListener(action);
		source.addMouseMotionListener(action);
		dragSource = source;
	}

	/**
	 * 返回拖拽源对象
	 * 
	 * @return
	 */
	public Component getDragSource() {
		return dragSource;
	}

	/**
	 * 用来画图形做跨组件的视觉效果的面板
	 * 
	 * @author 唐植超
	 * 
	 */
	static class ControlGhostGlassPane extends JPanel {

		private static final long serialVersionUID = 1L;
		private AlphaComposite composite;
		private BufferedImage dragged = null;
		private int state;
		private Point location = new Point(0, 0);

		public static final int STATE_ACCEPT = 1;
		public static final int STATE_UNACCEPT = 2;
		public static final int STATE_NOMAR = -1;

		public ControlGhostGlassPane() {
			setOpaque(false);
			// 半透明效果
			composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
					0.5f);
		}

		public void setState(int state) {
			this.state = state;
		}

		public void setImage(BufferedImage dragged) {
			this.dragged = dragged;
		}

		public void setPoint(Point location) {
			this.location = location;
		}

		public void paintComponent(Graphics g) {
			if (dragged == null) {
				return;
			}
			// 在蒙版上画图
			int x = (int) (location.getX() - (dragged.getWidth(this) / 2));
			int y = (int) (location.getY() - (dragged.getHeight(this) / 2));
			Graphics2D g2 = (Graphics2D) g;
			g2.setComposite(composite);
			g2.drawImage(dragged, x, y, null);

			switch (state) {
			case STATE_ACCEPT:
				g2.drawImage(right_img, x + dragged.getWidth(this), y, this);
				break;
			case STATE_UNACCEPT:
				g2.drawImage(wrong_img, x + dragged.getWidth(this), y, this);
				break;
			case STATE_NOMAR:
				break;
			}
		}
	}

	/**
	 * 拖拽动作
	 * 
	 * @author 唐植超
	 * 
	 */
	class DragAction implements MouseMotionListener, MouseListener {

		private ControlGhostGlassPane glassPane;
		private JComponent targetComp;
		private boolean flag;

		public DragAction(ControlGhostGlassPane glassPane, JComponent targetComp) {
			this.glassPane = glassPane;
			this.targetComp = targetComp;
			flag = false;
		}

		@Override
		public void mouseDragged(MouseEvent e) {
			flag = true;
			Component c = e.getComponent();
			Point p = (Point) e.getPoint().clone();
			SwingUtilities.convertPointToScreen(p, c);
			Point eventPoint = (Point) p.clone();
			SwingUtilities.convertPointFromScreen(p, glassPane);

			// 鼠标是否在目标控件的上方
			SwingUtilities.convertPointFromScreen(eventPoint, targetComp);
			int state = targetComp.contains(eventPoint) ? ControlGhostGlassPane.STATE_ACCEPT
					: ControlGhostGlassPane.STATE_UNACCEPT;
			glassPane.setState(state);
			glassPane.setPoint(p);
			glassPane.repaint();
		}

		@Override
		public void mouseMoved(MouseEvent e) {

		}

		@Override
		public void mouseClicked(MouseEvent e) {

		}

		@Override
		public void mouseEntered(MouseEvent e) {

		}

		@Override
		public void mouseExited(MouseEvent e) {

		}

		@Override
		public void mousePressed(MouseEvent e) {
			flag = false;
			Component c = e.getComponent();
			BufferedImage image = new BufferedImage(c.getWidth(),
					c.getHeight(), BufferedImage.TYPE_INT_ARGB);
			Graphics g = image.getGraphics();
			c.paint(g);

			glassPane.setVisible(true);

			Point p = (Point) e.getPoint().clone();
			SwingUtilities.convertPointToScreen(p, c);
			SwingUtilities.convertPointFromScreen(p, glassPane);

			glassPane.setPoint(p);
			glassPane.setImage(image);
			glassPane.repaint();
		}

		@Override
		public void mouseReleased(MouseEvent e) {
			if (!flag) {
				glassPane.setImage(null);
				glassPane.repaint();
				glassPane.setVisible(false);
				glassPane.setImage(null);
				return;
			}
			ControlDragManager.this.dragSource = null;
			System.gc();

			final Component c = e.getComponent();
			final Point p = (Point) e.getPoint().clone();
			SwingUtilities.convertPointToScreen(p, c);
			final Point eventPoint = (Point) p.clone();
			SwingUtilities.convertPointFromScreen(p, glassPane);
			SwingUtilities.convertPointFromScreen(eventPoint, targetComp);
			glassPane.setPoint(p);
			glassPane.repaint();
			/*
			 * 移动控件有一下几种情况
			 * 
			 * 1、控件在原来的位置之上，没动
			 * 
			 * 2、控件从原来的位置移动到目标控件的位置
			 * 
			 * 3、控件已经在目标的位置上，然后自动在目标的位置
			 * 
			 * 4、控件在目标的位置上移动的其他控件上
			 */
			// 还没有进入目标组件
			if (!c.getParent().equals(targetComp)
					&& !targetComp.contains(eventPoint)) {
				new Thread(new Runnable() {

					@Override
					public void run() {
						// 没有接受
						glassPane.setState(ControlGhostGlassPane.STATE_NOMAR);
						// 拿到组件的原始位置
						Point cp = (Point) c.getLocationOnScreen().clone();
						// 转成glass位置
						SwingUtilities.convertPointFromScreen(cp, glassPane);
						// 当前组件在glass上的位置
						int x = cp.x;
						int y = cp.y;
						// 拿到当前显示的图片的位置
						int cx = p.x - (c.getWidth() / 2);
						int cy = p.y - (c.getHeight() / 2);

						int subX = (x - cx) / 10;
						int subY = (y - cy) / 10;

						x = p.x - (c.getWidth() / 2);
						y = p.y - (c.getHeight() / 2);

						for (int i = 0; i < 11; i++) {
							x += subX;
							y += subY;
							if (i == 10) {
								x = cp.x;
								y = cp.y;
							}
							cx = x + (c.getWidth() / 2);
							cy = y + (c.getHeight() / 2);
							glassPane.setPoint(new Point(cx, cy));
							glassPane.repaint();
							try {
								Thread.sleep(20);
							} catch (Exception e2) {
								e2.printStackTrace();
							}
						}
						glassPane.setVisible(false);
						glassPane.setImage(null);
					}
				}).start();
				return;
			}
			// 移动到目标的位置
			if (!c.getParent().equals(targetComp)
					&& targetComp.contains(eventPoint)) {
				// 复制出新的组件
				Component obj = (Component) ObjectUtil.cloneObject(c);
				// 让他能拖动
				ControlDragManager.this.canDrag(obj);
				// 添加到目标位置
				targetComp.add(obj);
				Point p2 = (Point) e.getPoint().clone();
				SwingUtilities.convertPointToScreen(p2, c);
				SwingUtilities.convertPointFromScreen(p2, targetComp);
				int x = p2.x - obj.getWidth() / 2;
				int y = p2.y - obj.getHeight() / 2;
				obj.setLocation(x, y);
				targetComp.repaint();
				targetComp.updateUI();
				glassPane.setVisible(false);
				glassPane.setImage(null);
				return;
			}
			// 控件已经在目标的位置上，然后自动在目标的位置
			if (c.getParent().equals(targetComp)
					&& targetComp.contains(eventPoint)) {
				Point p2 = (Point) e.getPoint().clone();
				SwingUtilities.convertPointToScreen(p2, c);
				SwingUtilities.convertPointFromScreen(p2, targetComp);
				int x = p2.x - c.getWidth() / 2;
				int y = p2.y - c.getHeight() / 2;
				c.setLocation(x, y);
				targetComp.repaint();
				glassPane.setVisible(false);
				glassPane.setImage(null);
				return;
			}
			// 控件在目标的位置上移动的其他控件上
			if (c.getParent().equals(targetComp)
					&& !targetComp.contains(eventPoint)) {
				// 没有接受
				glassPane.setState(ControlGhostGlassPane.STATE_NOMAR);
				// 这里记录图形的
				new Thread(new Runnable() {
					@Override
					public void run() {
						// 组件在glass上的位置
						Point cp = (Point) c.getLocationOnScreen().clone();
						// 转成glass位置
						SwingUtilities.convertPointFromScreen(cp, glassPane);
						// 拿到组件的位置
						int x = cp.x;
						int y = cp.y;
						// 拿到当前鼠标的位置
						int cx = eventPoint.x;
						int cy = eventPoint.y;

						int subX = (x - cx) / 10;
						int subY = (y - cy) / 10;

						x = eventPoint.x;
						y = eventPoint.y;

						for (int i = 0; i < 11; i++) {
							x += subX;
							y += subY;

							if (i == 10) {
								x = cp.x;
								y = cp.y;
							}

							cx = x + (c.getWidth() / 2);
							cy = y + (c.getHeight() / 2);
							glassPane.setPoint(new Point(cx, cy));
							glassPane.repaint();
							try {
								Thread.sleep(20);
							} catch (Exception e2) {
								e2.printStackTrace();
							}
						}
						glassPane.setVisible(false);
						glassPane.setImage(null);
					}
				}).start();
			}
		}
	}
}
